#include <time.h>
#include <sys/timeb.h>
#include <winsock2.h>
#include "MySQL/mysql.h"
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"

#include "Utils/T2List.h"
#include "CZoneHandler.h"
#include "CSpawnPointHandler.h"
#include "CLuaManager.h"
#include "CUtil.h"
#include "CMessagePacket.h"
#include "CMessagePacketTwo.h"
#include "CNpcPacket.h"
#include "CPacket.h"
#include "CDb.h"
#include "CLockOnPacket.h"
#include "CZone.h"
#include "CWeatherPacket.h"
#include "CVanaTime.h"
#include "Utils/CProfilingWindow.h"

/*std::vector<CNpc> CZoneHandler::npcZoneList[256];
std::vector<CNpc> CZoneHandler::mobZoneList[256];
std::vector<struct timer> CZoneHandler::npcTimerList[256];
std::vector<struct pathnode> CZoneHandler::pathNodeList[256];
// NEW
std::vector<CZone> CZoneHandler::m_zoneList;

short CZoneHandler::charsInZone[256];
CSemaphore* CZoneHandler::zoneCounterSemaphore[256];
CSemaphore* CZoneHandler::zoneActiveSemaphore[256];*/

CZoneHandler * CZoneHandler::_instance = NULL;

CZoneHandler::CZoneHandler(void) {
}

CZoneHandler::~CZoneHandler(void) {
}

CZone* CZoneHandler::getZone(unsigned short zoneID) {
	return &m_zoneList.at(zoneID);
}

CZoneHandler * CZoneHandler::getInstance() {

	if(_instance == NULL) {
		_instance = new CZoneHandler();
	}
	return _instance;
}

void CZoneHandler::loadZones() {

	for(int j = 0; j <= 0xFF; j++) {
		
		CZone *test = new CZone(j);
		m_zoneList.push_back(*test);

	}
}

int CZoneHandler::loadZoneList(unsigned short zone){
	MYSQL_RES * res;
	MYSQL_ROW row;
	MYSQL* mysqlConn;
	unsigned long size = 0, count, *lengths;
	char query[4096];
	
	if (zone > 255) {
		return 0;
	}

	CConsole::outTime("Loading %s...", CUtil::getZoneName(zone));
	
	//Clear the NPC list for the zone, incase this is a reload
	if((this->npcZoneList[zone].size() > 0)) {
		this->npcZoneList[zone].clear();
	}

	//Clear the MOB list for the zone, incase this is a reload
	if((this->mobZoneList[zone].size() > 0)) {
		this->mobZoneList[zone].clear();
	}

	//Clear the Region list for the zone, incase this is a reload
	if((CLuaManager::regionList[zone].size() > 0)) {
		CLuaManager::regionList[zone].clear();
	}


	//load NPCs and MOBs
	sprintf(query, 
		"SELECT npc.name, npc.id, npc.type, npc.type, npc.flag, npc.look, npc.animation,npc.alt_animation, npc.namevis, npc.speed, npc.alt_speed, npc.targid, npc.status, npc.unknown, npc.name_prefix, npc.pos_x, npc.pos_y, npc.pos_z, npc.pos_rot FROM `npc_list` AS npc WHERE npc.zone = %i ORDER BY id ASC",
			zone);

	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	char newName[512];

	if(!res) {
	//	CConsole::outErr("Zone 0x%X\t -> %s not found in DB!",zone ,CUtil::getZoneName(zone));
		//return 0;
	} else if(!mysql_num_rows(res)){
		mysql_free_result(res);
		//return size;
	} else {
		int npccnt = 0;
		int mobcnt = 0;

		int run = 0;
		CNpc* npc;
		while((row = mysql_fetch_row(res))) {
			npc = new CNpc();
			lengths = mysql_fetch_lengths(res);
			
			memset(npc->m_name, 0 , 17);

			memset(newName,0,512);
			memcpy(newName, row[0], lengths[0]);
			memcpy(npc->m_name,newName,16);

			npc->setId((unsigned int)atoi(row[1]));
			npc->setZone((unsigned char)zone);
			npc->type				= atoi(row[2]);

			npc->moveState			= 0;
			npc->flag				= atoi(row[4]);

			if(npc->type == 0x57) {
				memcpy(&npc->lookc, row[5], 20);
			}
			else {
				memcpy(&npc->look, row[5], 4);
			}

			npc->setAnimation(atoi(row[6]));
			npc->m_animationAlt	= atoi(row[7]);
			npc->namevis			= atoi(row[8]);
			npc->m_speed			= atoi(row[9]);
			npc->m_speedAlt		= atoi(row[10]);
			npc->setTargId(atoi(row[11]));
			npc->status			= atoi(row[12]);
			npc->unknown			= atoi(row[13]);

			npc->loc.p.x = (float) atof(row[15]);
			npc->loc.p.y = (float)atof(row[16]);
			npc->loc.p.z = (float)atof(row[17]);
			npc->loc.p.rotation = (unsigned short)atoi(row[18]);
			

			npc->incombat = false;
			npc->ownerId  = 0;
			npc->oldtime  = 0;
			npc->update   = false; 
			npc->timestamp = 0;
			npc->targ_p = npc->loc.p;

			npc->m_maxHp		= 100;
			npc->h.hp		= npc->getMaxHp();
		
			npc->m_objType	= OBJ_NPC;

			npc->sizeMod = 0;

			
			npccnt++;

			this->npcZoneList[zone].push_back(npc);
			npc = NULL;
		}
		//CConsole::outTime("Zone 0x%X\t -> %i NPCs %i MOBs loaded!",zone ,npccnt, mobcnt);
		mysql_free_result(res);
	}
	
	//CConsole::outTime("Zone 0x%X\t %s -> %i PATH %i SPAWN points loaded!",zone ,CUtil::getZoneName(zone) ,pathcnt, spawncnt);
	CLuaManager::OnZoneInitialize(zone);
	//CConsole::outTimePos(0,9, "                                               ");

	return 0;
}

unsigned int couNT = 0;

void CZoneHandler::spawnNpcs(CChar * schar) { 

	long double oldtime;
	int wait = 0;
	unsigned int i;
	bool onList = false;
	unsigned int couNT2 = 0;

	// Moghouse has no NPCs.  NPCs spawned from prev zone.
	unsigned char zone = schar->getDisplayZone();

	if((this->npcTimerList[zone].size() > 0)) 
	{
		for(i = 0; i < this->npcTimerList[zone].size(); i++) {
			oldtime = (long double)this->npcTimerList[zone].at(i).timestamp;
			wait = this->npcTimerList[zone].at(i).timetowait;

			if((oldtime + (wait)) < (long double)(time(NULL))) {
				switch(this->npcTimerList[zone].at(i).type) {

					case 0x01: {//doorclose
						CNpc * npc = (CNpc *)this->npcTimerList[zone].at(i).ptr;
						npc->closeDoor();
						this->npcTimerList[zone].erase(this->npcTimerList[zone].begin()+i,this->npcTimerList[zone].begin()+i+1);
						break;
					}
				}
			}
		}
	}
	
	if((this->npcZoneList[zone].size() < 1) ) {
		return;
	}

	//Reset couNT incase when zoning you go onto a map that has less mobs than what couNT was previously set to
	if (couNT > (this->npcZoneList[zone].size())) {
		couNT = 0;
	}

	int cnt = 0;
	for(i = couNT; couNT2 < 10;) {
		i = ((i + 1) % (this->npcZoneList[zone].size()));

		cnt++;
		CNpc* npc = this->npcZoneList[zone].at(i);

		//Reset the onList variable incase the last npc was on the last
		onList = false;

		// If Npc or Mob already spawned, don't spawn again.
		char found = schar->newSpawnIdList->find(npc->getId());
		if (found == 0x01) {
			onList = true;
		}

		// Always send a packet for NPCs that have alwaysZoneUpdate = TRUE
		if(npc->alwaysZoneUpdate) {
			if(!onList) {
				schar->quePacket(CNpcPacket(npc));
				//schar->spawnIDList.push_back(npc.m_id);
				schar->newSpawnIdList->pushBack(npc->getId(),(char)0x01);
			}
		}

		//We only need to generate packets if they need to be spawned or if they need to be de-spawned, otherwise its a waste
		else if(((!onList) && (CUtil::distance(schar->loc.p, npc->loc.p) < 60 )) || ((onList) && (CUtil::distance(schar->loc.p, npc->loc.p) > 60 ))) {
	
			couNT2++;

			if(!onList) {
				if(zone == schar->loc.prevzone) {
					// Inside moghouse only spawn moogle
					if((npc->loc.p.x == 0) && (npc->loc.p.y == 0) && (npc->loc.p.z == 1.5)){
						schar->quePacket(CNpcPacket(npc));
					}
					//schar->spawnIDList.push_back(npc.getId());
					schar->newSpawnIdList->pushBack(npc->getId(),(char)0x01);
				} else {
					// Spawn entire zone
					schar->quePacket(CNpcPacket(npc));
					// Add the npc to the list of spawned npcs of a char
					//schar->spawnIDList.push_back(npc.getId());
					schar->newSpawnIdList->pushBack(npc->getId(),(char)0x01);
				}
			}
			else if(onList) {
			//	npc->status = 0x07;
				schar->quePacket(CNpcPacket(npc, true));
				//Remove the mob we've just despawned from the list
				//schar->spawnIDList.erase(schar->spawnIDList.begin()+j);
				schar->newSpawnIdList->removeByKey(npc->getId());

			}
		}
		if(i == couNT || i == (couNT -1)) {
			break;
		}
	}
	couNT = i;
	char key[256];
	sprintf(key,"Npcs handled[zone: %d]",schar->getZone());
	CProfilingWindow::getInstance()->setValue(key, "%d/%d", cnt, this->npcZoneList[zone].size());
}

void CZoneHandler::removeMob(unsigned int mobid) {
	unsigned char zone = mobid >> 12;

	this->mobListSemaphore[zone]->forceLock();
	for(unsigned int i = 0; i < this->mobZoneList[zone].size(); i++) {

		if(this->mobZoneList[zone].at(i)->getId() == mobid) {
		//	this->mobZoneList[zone].at(i).m_pIdPoolEntry->pMob = NULL;
			this->mobZoneList[zone].erase(this->mobZoneList[zone].begin()+i,this->mobZoneList[zone].begin()+i+1);
			CConsole::outDebOnly("Removed mobid %x ",mobid);
			this->mobListSemaphore[zone]->forceUnlock();
			return;
		}

	}
	this->mobListSemaphore[zone]->forceUnlock();
}


int CZoneHandler::spawnMobs(unsigned char * data, unsigned short num, CChar * schar) {
	unsigned int acouNT = 0;
	unsigned long size = 0;
	unsigned int i;
	bool onList = false;
	unsigned int acouNT2 = 0;
	unsigned short zone = schar->getZone();

	this->mobListSemaphore[zone]->lock();
	this->spawnListSemaphore[zone]->lock();
	unsigned int cnt = 0;
	unsigned int mobCnt = (unsigned int)this->mobZoneList[zone].size();
	for(i = 0; i < mobCnt; i++) {
		cnt++;
		CNpc *mob = this->mobZoneList[zone].at(i);
		
		//Potential Crash fix
		if (mob == NULL) {
			continue;
		}

		//test mob aggro
		if((schar->getAnimation() != 5) && !(schar->h.nameFlag & FLAG_GM) && (mob->update == false) && mob->m_isAgressive && (mob->getAnimation() == 0x00) && (mob->status == 0x01) && (CUtil::distance(schar->loc.p, mob->loc.p) <= 25)) {
			int levelDif = schar->m_lvlMain - mob->m_lvlMain;

			if((levelDif < 10) && !schar->isDead()) {

				if(mob->hasBehaviour(AGGRO_SIGHT) && mob->isFaceing(schar->loc.p)) { // and in view and not usign invis
					if((CUtil::distance(schar->loc.p, mob->loc.p) <= 15 )) {
						mob->aggroChar(schar);
					}
				}
				if(mob->hasBehaviour(AGGRO_HEARING)) { // and not using sneak
					if((CUtil::distance(schar->loc.p, mob->loc.p) <= 8 )) {
						mob->aggroChar(schar);
					}
				} 
				if(mob->hasBehaviour(AGGRO_LOWHP) && (schar->getHpp() <= 66)) { // blood aggro 
					if((CUtil::distance(schar->loc.p, mob->loc.p) <= 20 )) {
						mob->aggroChar(schar);
					}
				}
				if(mob->hasBehaviour(AGGRO_MAGIC) && (schar->isCasting())) { // magic aggro
					if((CUtil::distance(schar->loc.p, mob->loc.p) <= 20 )) {
						mob->aggroChar(schar);
					}
				}

			} else {
				if(schar->isHealing()) {
					if(mob->hasBehaviour(AGGRO_SIGHT)) { // and in view and not usign invis
						if((CUtil::distance(schar->loc.p, mob->loc.p) <= 15 )) {
							mob->aggroChar(schar);
						}
					}
					if(mob->hasBehaviour(AGGRO_HEARING)) { // and not using sneak
						if((CUtil::distance(schar->loc.p, mob->loc.p) <= 8 )) {
							mob->aggroChar(schar);
						}
					} 
				}
				
			}
		
		}

		//Reset onList incase the previous mob was onList
		onList = false;

		//Speedup: avl for spawnIDList - Key: mob.id
		// If Npc or Mob already spawned, don't spawn again.
	
		
		char found = schar->newSpawnIdList->find(mob->getId());
		if (found == 0x01) {
			onList = true;
		}

		//We only need to generate packets if they need to be spawned or if they need to be de-spawned, otherwise its a waste
		if(((!onList) && (CUtil::distance(schar->getPos(), mob->getPos()) < 60 )) || ((onList) && (CUtil::distance(schar->getPos(), mob->getPos()) > 60 ))) {

			if(!onList) {
				schar->quePacket(CNpcPacket(mob));
				// Add the npc to the list of spawned npcs of a char
				schar->newSpawnIdList->pushBack(mob->getId(),(char)0x01);
				//schar->spawnIDList.push_back(mob->getId());
				
			}
			else if(onList) {
				CNpc * tmpMob = mob;

				//this is the problem for early despawns.

			//	tmpMob->status = 0x07;
				//Remove the mob we've just despawned from the list
				schar->newSpawnIdList->removeByKey(mob->getId());
				//schar->spawnIDList.erase(schar->spawnIDList.begin()+j);
				schar->quePacket(CNpcPacket(tmpMob, true));

			}
		}
	}

	char key[256];
	sprintf(key,"Mobs checked[zone: %d]",zone);
	CProfilingWindow::getInstance()->setValue(key, "%d/%d", cnt, this->mobZoneList[zone].size());

	this->mobListSemaphore[zone]->unlock();
	this->spawnListSemaphore[zone]->unlock();
	return 0;
	
}


CNpc* CZoneHandler::getNpc(int npcid, int zone) {

	if((this->npcZoneList[zone].size() < 1)) {
		return NULL;
	}

	for(unsigned int i = 0; i < this->npcZoneList[zone].size() ; i++) {
		if(this->npcZoneList[zone].at(i)->getId() == npcid) {
			return (this->npcZoneList[zone].at(i));
		}
	}

	return NULL;
}

CNpc * CZoneHandler::getMob(int npcid, int zone) {

	if((this->mobZoneList[zone].size() < 1)) {
		return NULL;
	}

	for(unsigned int i = 0; i < this->mobZoneList[zone].size() ; i++) {
		if(this->mobZoneList[zone].at(i)->getId() == npcid) {
			return (this->mobZoneList[zone].at(i));
		}
	}

	return NULL;
}

CNpc * CZoneHandler::getNpcByTarg(int npctargid, int zone) {

	if((this->npcZoneList[zone].size() < 1)) {
		return NULL;
	}

	for(unsigned int i = 0; i < this->npcZoneList[zone].size() ; i++) {
		if(this->npcZoneList[zone].at(i)->getTargId() == npctargid) {
			return (this->npcZoneList[zone].at(i));
		}
	}
	return NULL;
}

CNpc * CZoneHandler::getMobByTarg(int mobtargid, int zone) {

	if((this->mobZoneList[zone].size() < 1)) {
		return NULL;
	}

	for(unsigned int i = 0; i < this->mobZoneList[zone].size() ; i++) {
		if(this->mobZoneList[zone].at(i)->getTargId() == mobtargid) {
			return (this->mobZoneList[zone].at(i));
		}
	}
	return NULL;
}


void CZoneHandler::initZoneSemaphors(unsigned short zone) {
	if (zone>255) {
		CConsole::outErr("Tried to init semaphore for Zone > 255");
	}

	//disable access to charsInZone Variable
	this->zoneCounterSemaphore[zone] = new CSemaphore(1, false);
	
	//set zone to sleep
	this->zoneActiveSemaphore[zone] = new CSemaphore(0, false);

	this->mobListSemaphore[zone] = new CSemaphore(1,true);
	this->spawnListSemaphore[zone] = new CSemaphore(1,true);

	//there are 0 players in this zone, -1 indicates that the thread has not been started yet
	this->charsInZone[zone]=-1;
}

void CZoneHandler::increaseZoneCounter(unsigned short zone) {
	//lock charsInZone Variable
	unsigned short zid = zone;
	this->zoneCounterSemaphore[zone]->forceLock();

	if (this->charsInZone[zone] == -1) {
		CConsole::outDebOnly("Starting ZoneServ thread for zone %d",zone);
		CreateThread(0,0,ZoneServ,(LPVOID)zid,0,0);
		this->charsInZone[zone]++;
	}

	//increase the number of chars in this zone
	this->charsInZone[zone]++;

	//Check if someone entered a zone that was sleeping before
	if (this->charsInZone[zone]==1) {
		//Wake up the ZoneServer for this zone
		CConsole::outDebOnly("Thread %u unlocks zone %.2X.",GetCurrentThreadId(),zone);
		this->zoneActiveSemaphore[zone]->forceUnlock();
	}

	//unlock charsInZone Variable
	CZoneHandler::zoneCounterSemaphore[zone]->forceUnlock();
}

void CZoneHandler::decreaseZoneCounter(unsigned short zone) {
	//lock charsInZone Variable
	this->zoneCounterSemaphore[zone]->forceLock();

	//Check to see if we need to modify the chars in zone variable
	if (this->charsInZone[zone]==0) {
		CConsole::outErr("Trying to decrease the #chars in zone %d below zero!",zone);
	} else {
		//decrease the number of chars in this zone
		this->charsInZone[zone]--;

		//Check if the last player left the zone
		if (this->charsInZone[zone]==0) {
			
			for(unsigned int i = 0; i < CZoneHandler::getInstance()->mobZoneList[zone].size(); i++) {
				CNpc * mob = CZoneHandler::getInstance()->mobZoneList[zone].at(i);
				mob->checkAction();
			}
			//Send ZoneServer for this zone to sleep
			CConsole::outDebOnly("Thread %u locks zone %.2X.",GetCurrentThreadId(),zone);
			this->zoneActiveSemaphore[zone]->forceLock();
		}
	}
	//unlock charsInZone Variable
	this->zoneCounterSemaphore[zone]->forceUnlock();
}

void CZoneHandler::checkZoneActive(unsigned short zone) {
	this->zoneActiveSemaphore[zone]->forceLock(false);
	this->zoneActiveSemaphore[zone]->forceUnlock();
}

int CZoneHandler::changeZoneWeather(unsigned short zoneID) {
	CZone * zone = this->getZone(zoneID);

	time_t currentTime;
	FILE * pFile;

	unsigned char data;
	unsigned int weatherDay = 0;
	unsigned int weatherChangeTime = 0;
	unsigned int weatherOffset = 0;

	time(&currentTime);

	// Find the timestamp since the start of vanadiel
	weatherChangeTime = ((long)currentTime - 1009810800);

	// Calculate the earth seconds for the vanadiel time
	weatherChangeTime = (weatherDay % 7464960);

	// Convert earth seconds to vanadiel minutes
	weatherChangeTime = (unsigned int)(weatherDay / 2.4);

	// Find the timestamp since the start of vanadiel
	weatherDay = ((long)currentTime - 1009810800); 

	// Calculate what day we are on since the start of vanadiel time
	//		1 Vana'diel Day = 57 minutes 36 seconds or 3456 seconds
	weatherDay = weatherDay / 3456;

	// The weather starts over again every 2160 days
	weatherDay = weatherDay % 2160;

	//Random time between 3 minutes and 30 minutes for the next weather change
	zone->setNextWeatherChange((unsigned long long)(currentTime+180+(rand()%1620)));

	pFile = fopen("./weatherinfo.dat","rb");
	if (pFile != NULL) {
			// 3 weather possibilities per day, 2160 weather days per zone
			weatherOffset = (zoneID * 2160 * 3) + (weatherDay * 3);

			// Get a random number to determine which weather effect we will use
			unsigned int random_weather = (int)(rand()%100);

			// 15% chance for rare weather, 35% chance for common weather, 50% chance for normal weather
			//	  *Percentages were generated from a 6 hour sample and rounded down to closest multiple of 5*
			if (random_weather <= 15) { //15% chance to have the weather_Chance			
				weatherOffset += 2;
			} else if (random_weather <= 50) { // 35% chance to have weather_common			
				weatherOffset++;
			}
			
			// Seek to the position for this weather day on this zone
			fseek(pFile,weatherOffset,SEEK_SET);

			// Read the weather effect from the file
			fread(&data, 1, 1, pFile);

			//Closes weather data file
			fclose(pFile);

			weather newWeather;

			// Fog in the morning between the hours of 2 and 7 if there is not a specific elemental weather to override it
			unsigned int hour;
			unsigned int minute;
			CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
			if ( (hour >= 2) && (hour < 7) && (unsigned short)data < 0x04) {
				newWeather.weatherEffect = 3;
				//Force the weather to change by 7 am
				//	2.4 vanadiel minutes = 1 earth second
				//	60 vanadiel minutes = 1 vanadiel hour
				zone->setNextWeatherChange((unsigned long long)(currentTime+ ((7-hour)*2.4*60-(minute*2.4))));
			} else {
				// Set the new weather to the value found from the file
				newWeather.weatherEffect = (unsigned short)data;
			}

			// Set the change time of the weather effect (in vanadiel minutes)
			newWeather.changeTime = weatherChangeTime;

			// Saves the current weather
			zone->setZoneWeather(newWeather);

			//Sends everyone on the zone the new weather
			CChar *update = new CChar;
			update->setZone(zoneID);
			update->insertPacket(CHAR_INZONE,CWeatherPacket(newWeather));
			delete update;
	}

	return 0;
}


DWORD WINAPI ZoneServ(LPVOID lpParam) {
	unsigned short zone = (unsigned short)lpParam;

	unsigned int timeBetweenUpdates = 1000/5; //200 ms - zone will be checked 5 times per second
	float enmityDecay = 60 / 5; //12 every 200 ms, = 60 per second
	unsigned long long startTime = 0;
	unsigned long long endTime = 0;
	unsigned long long diff = 0;
	struct __timeb64 currSysTime;

	while(1) 
	{
		//Check if at least 1 player is in the zone, thread will block on wait till at least 1 is in zone
		CZoneHandler::getInstance()->checkZoneActive(zone);
		
		_ftime(&currSysTime);
		startTime = (currSysTime.time*1000) + currSysTime.millitm;

			

		CZoneHandler::getInstance()->spawnListSemaphore[zone]->lock();
		for(unsigned int i = 0; i < CZoneHandler::getInstance()->mobSpawnZoneList[zone].size(); i++) {

		//	CConsole::outTime("HANDLING %i",CZoneHandler::getInstance()->mobSpawnZoneList[zone].at(i)->m_groupId);

			CZoneHandler::getInstance()->mobSpawnZoneList[zone].at(i)->checkSpawnPoints();

		}
		CZoneHandler::getInstance()->spawnListSemaphore[zone]->unlock();

		CZoneHandler::getInstance()->mobListSemaphore[zone]->lock();
		for(unsigned int i = 0; i < CZoneHandler::getInstance()->mobZoneList[zone].size(); i++) {
			CNpc * mob = CZoneHandler::getInstance()->mobZoneList[zone].at(i);
			mob->checkAction();
			mob->decayVEnmity(enmityDecay);
		}
		CZoneHandler::getInstance()->mobListSemaphore[zone]->unlock();

		for(unsigned int i = 0; i < CZoneHandler::getInstance()->npcZoneList[zone].size(); i++) {
			CNpc * npc = CZoneHandler::getInstance()->npcZoneList[zone].at(i);
			npc->checkAction();
		}
	//	CSpawnPointHandler::unlockSpawnPointList(zone);

		unsigned long long currentTime = currSysTime.time;
		if (CZoneHandler::getInstance()->getZone(zone)->getNextWeatherChange() < currentTime) {
			CZoneHandler::getInstance()->changeZoneWeather(zone);
		}
		_ftime(&currSysTime);
		endTime = (currSysTime.time*1000) + currSysTime.millitm;
		diff = endTime - startTime;

		if (diff < timeBetweenUpdates) {
			Sleep((DWORD)(timeBetweenUpdates-diff));
		} else {
			CConsole::outDebOnly("Warning: Zone %d took %u milliseconds to handle all mobs & npcs",zone,diff);
		}
	}
	return 0;
}


void CZoneHandler::loadSpawnGroups(unsigned short zone) {


	MYSQL* mysqlConn;
	int count;
	char query[4096];
	char subquery[4096];
	char subquery2[4096];

	MYSQL_RES * res;
	MYSQL_ROW row;


	int oldzone = 0;
	int oldpoolid = 0;

	memset(query,0,4096);
	memset(subquery,0,4096);
	memset(subquery2,0,4096);

	//old query:
	sprintf(query, "SELECT groupid FROM spawn_group WHERE zoneid=%d", zone);

	//Masterquery: 
	//we could even load all zones at once :P
	/*
	sprintf(subquery, "SELECT groupid FROM spawn_group WHERE zoneid=%d", zone);

	sprintf(subquery2,    "SELECT sq1.groupid, linkid, poolid, respawntime, flags, maxspawns, dropid, minLevel, maxLevel");
	sprintf(subquery2, "%s FROM (%s) as sq1 LEFT JOIN spawn_grouptopool as sgtp", subquery2, subquery);
	sprintf(subquery2, "%s ON (sq1.groupid = sgtp.groupid)", subquery2);
	sprintf(subquery2, "%s WHERE poolid is not NULL", subquery2);

	//CConsole::outPlain(subquery2);

	sprintf(query,    "SELECT sq2.groupid, poolid, spawnid, linkid,  respawntime, flags, maxspawns, dropid, minLevel, maxLevel, pos_x, pos_y, pos_z, pos_rot");
	sprintf(query, "%s FROM (%s) as sq2 LEFT JOIN spawn_point as sp",query, subquery2);
	sprintf(query, "%s ON (sq2.groupid = sp.groupid)",query);
	sprintf(query, "%s WHERE (spawnid is not NULL)", query);
	sprintf(query, "%s ORDER BY sq2.groupid, poolid, spawnid", query);
	*/

	//CConsole::outPlain(query);

	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if( (res!=NULL) && (mysql_num_rows(res) > 0) ) {

		while((row = mysql_fetch_row(res))) {
			CSpawnPointGroup * pNewGroup = new CSpawnPointGroup();

			pNewGroup->m_groupId = atoi(row[0]);
			pNewGroup->m_zoneId = zone;

			// load all mob relations
			if(!pNewGroup->loadSpawnMobPool()) {
				continue;
			}
			// load all spawnpoints assigned to the group
			if(!pNewGroup->loadSpawnPoints()) {
			//	continue;
			}

			this->mobSpawnZoneList[zone].push_back(pNewGroup);

		}

	}
	if (res!=NULL)
		mysql_free_result(res);


}

